Redis 很多人都用過,拿來做快取、工作佇列、分散式鎖都很順手。但我一直覺得只停在「會用」有點可惜,尤其是它背後的網路 I/O、RESP 協定、記憶體管理和資料結構,其實都很值得拆開來看。
所以這 30 天我打算用 Go 語言 寫一個 Redis Clone。不是追求功能百分之百相容,而是把一個記憶體型 Key-Value 資料庫該有的骨架真的做出來。
選 Go 的原因很直接:
net 標準庫已經把很多底層細節包好了,我可以把心力放在 server 架構上。為了不要寫到一半迷路,我先把 30 天拆成五個階段。至少先有一張地圖,後面每天才知道自己在補哪一塊:
第一階段:網路與協定解析 (Day 1 - 5)
│
▼
第二階段:記憶體資料庫引擎 (Day 6 - 12)
│
▼
第三階段:命令處理器與事件迴圈 (Day 13 - 18)
│
▼
第四階段:資料持久化 AOF/RDB (Day 19 - 24)
│
▼
第五階段:進階特性與效能調優 (Day 25 - 30)
GET、SET、DEL、EXISTS、INCR 等常見命令。探討 Redis 的single-thread 事件迴圈模型,並在 Go 中設計合適的鎖競爭優化方案。說實在,一開始想說不就是個 map,後來查資料才發現要處理的事還真多,光是把架構圖畫出來就覺得有硬仗要打。
現在,就動手初始化專案吧。我把專案命名為 redis-clone。
我規劃的目錄結構如下,兼顧了職責分離與 Go 社群的專案設計規範:
redis-clone/
├── articles/ # 存放 30 天鐵人賽文章
├── code/ # 專案原始碼
│ ├── go.mod # Go Module 定義檔
│ ├── main.go # 程式主入口
│ ├── server/ # TCP 網路伺服器模組
│ │ └── server.go
│ └── resp/ # RESP 協定解析模組
│ ├── resp.go # 資料結構定義
│ ├── parser.go # 解析器實作
│ └── parser_test.go # 單元測試
在 code/ 資料夾下,執行 Go Module 初始化命令:
cd code
go mod init redis-clone
這會產生一個基礎的 go.mod 檔案:
module redis-clone
go 1.26.2
今天的進度主要是建好專案骨架,還沒有可以直接執行的功能。我們先確認一下目前的目錄結構是不是正確:
$ tree code/
code/
├── db
│ └── database.go
├── go.mod
├── main.go
└── resp
└── parser.go
(或者用 ls -R code/ 也可以看到建立的檔案)
如果有出現這些檔案,代表第一天的準備工作就完成了!
第一天先把專案範圍和目錄骨架定下來。雖然還沒寫出可以跑的功能,但至少知道接下來 29 天要補哪些東西。
明天開始碰 TCP Server。連線管理和 Graceful Shutdown 聽起來很基本,但我猜真正寫起來應該不會太輕鬆,明天見!